#include "D3dInitializer.h"

D3dInitializer::D3dInitializer(HWND windowHandle, UINT windowWidth, UINT windowHeight) : m_windowHandle(windowHandle),
	m_windowWidth(windowWidth),
	m_windowHeight(windowHeight),
	m_renderHandle()
{
	m_renderHandle.d3dContext = nullptr;
	m_renderHandle.d3dDevice = nullptr;
	m_renderHandle.swapChain = nullptr;
}

D3dInitializer::~D3dInitializer(void)
{
	ClearD3D();
}

HRESULT D3dInitializer::InitializeD3D( UINT antiAliasingCount = 1, UINT antiAliasingQuality = 0 )
{
	HRESULT result = S_OK;
	UINT createDeviceFlags = 0;
#ifdef _DEBUG
	createDeviceFlags |= D3D11_CREATE_DEVICE_DEBUG;
#endif
	D3D_DRIVER_TYPE driverTypes[] =
	{
		D3D_DRIVER_TYPE_HARDWARE,
		D3D_DRIVER_TYPE_WARP,
		D3D_DRIVER_TYPE_REFERENCE,
	};
	UINT numDriverTypes = ARRAYSIZE( driverTypes );

	D3D_FEATURE_LEVEL featureLevels[] =
	{
		D3D_FEATURE_LEVEL_11_1,
		D3D_FEATURE_LEVEL_11_0,
		D3D_FEATURE_LEVEL_10_1,
		D3D_FEATURE_LEVEL_10_0,
	};
	UINT numFeatureLevels = ARRAYSIZE( featureLevels );

	DXGI_SWAP_CHAIN_DESC swapChainDesc;
	ZeroMemory( &swapChainDesc, sizeof( swapChainDesc ) );
	swapChainDesc.BufferCount = 1;
	swapChainDesc.BufferDesc.Width = m_windowWidth;
	swapChainDesc.BufferDesc.Height = m_windowHeight;
	swapChainDesc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
	swapChainDesc.BufferDesc.RefreshRate.Numerator = 60;
	swapChainDesc.BufferDesc.RefreshRate.Denominator = 1;
	swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
	swapChainDesc.OutputWindow = m_windowHandle;
	swapChainDesc.SampleDesc.Count = antiAliasingCount;
	swapChainDesc.SampleDesc.Quality = antiAliasingQuality;
	swapChainDesc.Windowed = TRUE;
	D3D_DRIVER_TYPE driverType = D3D_DRIVER_TYPE_NULL;
	D3D_FEATURE_LEVEL featureLevel = D3D_FEATURE_LEVEL_11_1;
	for( UINT driverTypeIndex = 0; driverTypeIndex < numDriverTypes; driverTypeIndex++ )
	{
		driverType = driverTypes[driverTypeIndex];
		result = D3D11CreateDeviceAndSwapChain( nullptr,
			driverType,
			nullptr,
			createDeviceFlags,
			featureLevels,
			numFeatureLevels,
			D3D11_SDK_VERSION,
			&swapChainDesc,
			&m_renderHandle.swapChain,
			&m_renderHandle.d3dDevice,
			&featureLevel,
			&m_renderHandle.d3dContext );
		if ( result == E_INVALIDARG )
		{
			// DirectX 11.0 platforms will not recognize D3D_FEATURE_LEVEL_11_1 so we need to retry without it
			result = D3D11CreateDeviceAndSwapChain( nullptr,
				driverType,
				nullptr,
				createDeviceFlags,
				&featureLevels[1],
				numFeatureLevels - 1,
				D3D11_SDK_VERSION,
				&swapChainDesc, 
				&m_renderHandle.swapChain,
				&m_renderHandle.d3dDevice,
				&featureLevel,
				&m_renderHandle.d3dContext );
		}
		if( SUCCEEDED( result ) )
		{
			break;
		}
	}
	if( FAILED( result ) )
	{
		return result;
	}
	return S_OK;
}

void D3dInitializer::ClearD3D( void )
{
	m_renderHandle.d3dDevice->Release();
	m_renderHandle.d3dContext->Release();
	m_renderHandle.swapChain->Release();
}

D3dRenderHandle* D3dInitializer::GetRenderHandle( void )
{
	return &m_renderHandle;
}

ID3D11RenderTargetView* D3dInitializer::CreateRenderTargetView()
{
	ID3D11RenderTargetView* renderTargetView;
	ID3D11Texture2D* backBuffer = nullptr;
	HRESULT result = m_renderHandle.swapChain->GetBuffer( 0, __uuidof( ID3D11Texture2D ), ( LPVOID* )&backBuffer );
	if( FAILED( result ) )
	{
		return nullptr;
	}
	result = m_renderHandle.d3dDevice->CreateRenderTargetView( backBuffer, nullptr,  &renderTargetView );
	backBuffer->Release();
	if( FAILED( result ) )
	{
		return nullptr;
	}
	return renderTargetView;
}
